using System;
using System.Collections.Generic;
using System.Reflection;
using System.ComponentModel;
using System.Threading.Tasks;
using Opc.Ua;
using System.Linq;

namespace twistmachinev2
{
    /// <summary>
    /// OPCUA节点映射特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class OpcUaNodeAttribute : Attribute
    {
        /// <summary>
        /// 节点ID
        /// </summary>
        public string NodeId { get; set; }

        /// <summary>
        /// 是否自动订阅
        /// </summary>
        public bool AutoSubscribe { get; set; } = true;

        /// <summary>
        /// 是否可写
        /// </summary>
        public bool CanWrite { get; set; } = false;

        public OpcUaNodeAttribute(string nodeId)
        {
            NodeId = nodeId;
        }
    }

    /// <summary>
    /// OPCUA数据映射器
    /// </summary>
    public class OpcUaMapper
    {
        private readonly OpcUaClient opcUaClient;
        private readonly OpcUaConfigManager configManager;
        private readonly Dictionary<string, PropertyInfo> propertyMappings;
        private readonly Dictionary<string, object> targetObjects;

        public OpcUaMapper(OpcUaClient client, OpcUaConfigManager config)
        {
            opcUaClient = client;
            configManager = config;
            propertyMappings = new Dictionary<string, PropertyInfo>();
            targetObjects = new Dictionary<string, object>();
        }

        /// <summary>
        /// 注册映射对象
        /// </summary>
        /// <param name="target">目标对象</param>
        /// <param name="objectId">对象标识符</param>
        public void RegisterObject(object target, string objectId = null)
        {
            if (target == null)
                throw new ArgumentNullException(nameof(target));

            string id = objectId ?? target.GetType().Name;
            targetObjects[id] = target;

            // 扫描属性并建立映射关系
            ScanAndMapProperties(target, id);
        }

        /// <summary>
        /// 扫描对象属性并建立映射关系
        /// </summary>
        /// <param name="target">目标对象</param>
        /// <param name="objectId">对象ID</param>
        private void ScanAndMapProperties(object target, string objectId)
        {
            Type targetType = target.GetType();
            PropertyInfo[] properties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo property in properties)
            {
                // 检查是否有OpcUaNode特性
                OpcUaNodeAttribute nodeAttr = property.GetCustomAttribute<OpcUaNodeAttribute>();
                if (nodeAttr != null)
                {
                    string mappingKey = $"{objectId}.{property.Name}";
                    propertyMappings[nodeAttr.NodeId] = property;

                    // 如果启用自动订阅，则订阅该节点
                    if (nodeAttr.AutoSubscribe)
                    {
                        SubscribeToNode(nodeAttr.NodeId, target, property);
                    }
                }
                else
                {
                    // 检查配置文件中是否有对应的映射
                    var nodeConfig = configManager.GetAllNodes().Find(n => n.PropertyName == property.Name);
                    if (nodeConfig != null)
                    {
                        string mappingKey = $"{objectId}.{property.Name}";
                        propertyMappings[nodeConfig.NodeId] = property;

                        // 如果配置为订阅，则订阅该节点
                        if (nodeConfig.IsSubscribed)
                        {
                            SubscribeToNode(nodeConfig.NodeId, target, property);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 订阅节点数据变化
        /// </summary>
        /// <param name="nodeId">节点ID</param>
        /// <param name="target">目标对象</param>
        /// <param name="property">属性信息</param>
        private void SubscribeToNode(string nodeId, object target, PropertyInfo property)
        {
            opcUaClient.Subscribe(nodeId, (id, value) =>
            {
                try
                {
                    // 收到订阅数据
                    SetPropertyValue(target, property, value);
                }
                catch (Exception ex)
                {
                    // 设置属性值时发生错误
                }
            });
        }

        /// <summary>
        /// 设置属性值
        /// </summary>
        /// <param name="target">目标对象</param>
        /// <param name="property">属性信息</param>
        /// <param name="value">值</param>
        private void SetPropertyValue(object target, PropertyInfo property, object value)
        {
            if (!property.CanWrite)
                return;

            try
            {
                // 类型转换
                object convertedValue = ConvertValue(value, property.PropertyType);
                property.SetValue(target, convertedValue);

                // 触发属性变化通知（如果对象实现了INotifyPropertyChanged）
                if (target is INotifyPropertyChanged notifyTarget)
                {
                    var propertyChangedEvent = target.GetType().GetEvent("PropertyChanged");
                    if (propertyChangedEvent != null)
                    {
                        var args = new PropertyChangedEventArgs(property.Name);
                        propertyChangedEvent.GetRaiseMethod()?.Invoke(target, new object[] { target, args });
                    }
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"无法设置属性 {property.Name} 的值: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 类型转换
        /// </summary>
        /// <param name="value">原始值</param>
        /// <param name="targetType">目标类型</param>
        /// <returns>转换后的值</returns>
        private object ConvertValue(object value, Type targetType)
        {
            if (value == null)
                return null;

            // 如果类型相同，直接返回
            if (targetType.IsAssignableFrom(value.GetType()))
                return value;

            // 处理可空类型
            Type underlyingType = Nullable.GetUnderlyingType(targetType);
            if (underlyingType != null)
            {
                if (value == null)
                    return null;
                targetType = underlyingType;
            }

            // 使用Convert类进行转换
            try
            {
                return Convert.ChangeType(value, targetType);
            }
            catch
            {
                // 如果转换失败，尝试使用TypeConverter
                TypeConverter converter = TypeDescriptor.GetConverter(targetType);
                if (converter.CanConvertFrom(value.GetType()))
                {
                    return converter.ConvertFrom(value);
                }
                throw;
            }
        }

        /// <summary>
        /// 读取所有映射的属性值
        /// </summary>
        /// <param name="objectId">对象ID</param>
        /// <returns>读取结果</returns>
        public async Task<OperateResult> ReadAllPropertiesAsync(string objectId)
        {
            if (!targetObjects.ContainsKey(objectId))
                return new OperateResult($"未找到对象: {objectId}");

            object target = targetObjects[objectId];
            List<NodeId> nodeIds = new List<NodeId>();
            List<PropertyInfo> properties = new List<PropertyInfo>();


            foreach (var mapping in propertyMappings)
            {
                NodeId nodeId = new NodeId(mapping.Key);
                nodeIds.Add(nodeId);
                properties.Add(mapping.Value);
            }

            if (nodeIds.Count == 0)
                return new OperateResult();

            // 批量读取
           
            var readResult = await opcUaClient.ReadAsync(nodeIds.ToArray());
            if (!readResult.IsSuccess)
                return new OperateResult(readResult.Message);

            // 设置属性值
            for (int i = 0; i < readResult.Content.Length && i < properties.Count; i++)
            {
                try
                {
                    SetPropertyValue(target, properties[i], readResult.Content[i]);
                }
                catch (Exception ex)
                {
                    // 设置属性值时发生错误
                }
            }

            return new OperateResult();
        }

        /// <summary>
        /// 写入属性值到OPCUA节点
        /// </summary>
        /// <param name="objectId">对象ID</param>
        /// <param name="propertyName">属性名称</param>
        /// <returns>写入结果</returns>
        public async Task<OperateResult> WritePropertyAsync(string objectId, string propertyName)
        {
            if (!targetObjects.ContainsKey(objectId))
                return new OperateResult($"未找到对象: {objectId}");

            object target = targetObjects[objectId];
            PropertyInfo property = target.GetType().GetProperty(propertyName);
            
            if (property == null)
                return new OperateResult($"未找到属性: {propertyName}");

            // 查找对应的节点ID
            string nodeId = null;
            foreach (var mapping in propertyMappings)
            {
                if (mapping.Value == property)
                {
                    nodeId = mapping.Key;
                    break;
                }
            }

            if (string.IsNullOrEmpty(nodeId))
                return new OperateResult($"属性 {propertyName} 未映射到OPCUA节点");

            // 获取属性值并写入
            object value = property.GetValue(target);
            return await opcUaClient.WriteAsync(nodeId, value);
        }

        /// <summary>
        /// 写入所有可写属性到OPCUA节点
        /// </summary>
        /// <param name="objectId">对象ID</param>
        /// <returns>写入结果</returns>
        public async Task<OperateResult> WriteAllPropertiesAsync(string objectId)
        {
            if (!targetObjects.ContainsKey(objectId))
                return new OperateResult($"未找到对象: {objectId}");

            object target = targetObjects[objectId];
            List<string> nodeIds = new List<string>();
            List<object> values = new List<object>();

            // 收集可写的属性值
            foreach (var mapping in propertyMappings)
            {
                var nodeConfig = configManager.GetNodeConfig(mapping.Key);
                if (nodeConfig?.CanWrite == true)
                {
                    object value = mapping.Value.GetValue(target);
                    nodeIds.Add(mapping.Key);
                    values.Add(value);
                }
            }

            if (nodeIds.Count == 0)
                return new OperateResult();

            // 批量写入
            return await opcUaClient.WriteAsync(nodeIds.ToArray(), values.ToArray());
        }

        /// <summary>
        /// 建立所有订阅
        /// </summary>
        public void EstablishAllSubscriptions()
        {
            foreach (var targetObj in targetObjects)
            {
                string objectId = targetObj.Key;
                object target = targetObj.Value;
                Type targetType = target.GetType();
                
                // 获取所有带有OpcUaNode特性的属性
                var properties = targetType.GetProperties()
                    .Where(p => p.GetCustomAttribute<OpcUaNodeAttribute>() != null)
                    .ToArray();
                
                foreach (var property in properties)
                {
                    var nodeAttr = property.GetCustomAttribute<OpcUaNodeAttribute>();
                    if (nodeAttr != null && nodeAttr.AutoSubscribe)
                    {
                        SubscribeToNode(nodeAttr.NodeId, target, property);
                    }
                }
                
                // 同时检查配置文件中的订阅设置
                var allNodes = configManager.GetAllNodes();
                foreach (var nodeConfig in allNodes.Where(n => n.IsSubscribed))
                {
                    var property = targetType.GetProperty(nodeConfig.PropertyName);
                    if (property != null)
                    {
                        SubscribeToNode(nodeConfig.NodeId, target, property);
                    }
                }
            }
        }
        
        /// <summary>
        /// 取消所有订阅
        /// </summary>
        public void UnsubscribeAll()
        {
            foreach (var mapping in propertyMappings)
            {
                opcUaClient.Unsubscribe(mapping.Key);
            }
        }

        /// <summary>
        /// 获取映射信息
        /// </summary>
        /// <returns>映射信息字典</returns>
        public Dictionary<string, string> GetMappingInfo()
        {
            var info = new Dictionary<string, string>();
            foreach (var mapping in propertyMappings)
            {
                info[mapping.Key] = mapping.Value.Name;
            }
            return info;
        }
    }
}